﻿//----------------------------------------------------------------
// Class Utility for Crypting data (Encrypt and Decrypt)
// * Date Created	: 28/02/2007
// * Created by		: Toan Vo
// * Review by		: None
// * Third Party	: None
//----------------------------------------------------------------

using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Security.Cryptography;

namespace Pharmacy.Ultilities
{
    /// <summary>
    /// Summary description for CryptHelper.
    /// </summary>
    public sealed class CryptHelper
    {
        private static string fKeyCrypt = "!#$?8l'^";
        public CryptHelper()
        {
        }

        /// <summary>
        /// Encrypts the specified p to encrypt with default key.
        /// </summary>
        /// <param name="pToEncrypt">The p to encrypt.</param>
        /// <returns></returns>
        public static string Encrypt(string pToEncrypt)
        {
            return Encrypt(pToEncrypt, fKeyCrypt);
        }

        /// <summary>
        /// Encrypts the specified p to encrypt has key.
        /// </summary>
        /// <param name="pToEncrypt">The p to encrypt.</param>
        /// <param name="psKey">The ps key.</param>
        /// <returns></returns>
        public static string Encrypt(string pToEncrypt, string psKey)
        {
            if (pToEncrypt == string.Empty && pToEncrypt == null && Convert.IsDBNull(pToEncrypt))
            {
                return pToEncrypt;
            }
            try
            {
                DESCryptoServiceProvider aDes = new DESCryptoServiceProvider();
                byte[] inputByteArray = Encoding.UTF8.GetBytes(pToEncrypt);
                byte[] aKey = ASCIIEncoding.ASCII.GetBytes(psKey);
                aDes.Key = aKey;
                aDes.IV = aKey;

                MemoryStream aMemoryStream = new MemoryStream();
                CryptoStream aCryptoStream = new CryptoStream(aMemoryStream, aDes.CreateEncryptor(), CryptoStreamMode.Write);
                aCryptoStream.Write(inputByteArray, 0, inputByteArray.Length);
                aCryptoStream.FlushFinalBlock();
                StringBuilder aSb = new StringBuilder();

                foreach (byte bByte in aMemoryStream.ToArray())
                {
                    aSb.AppendFormat("{0:X2}", bByte);
                }
                return aSb.ToString();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Decrypts the specified p to decrypt.
        /// </summary>
        /// <param name="pToDecrypt">The p to decrypt.</param>
        /// <returns></returns>
        public static string Decrypt(string pToDecrypt)
        {
            return Decrypt(pToDecrypt, fKeyCrypt);
        }

        /// <summary>
        /// Decrypts the specified p to decrypt.
        /// </summary>
        /// <param name="pToDecrypt">The p to decrypt.</param>
        /// <param name="psKey">The ps key.</param>
        /// <returns></returns>
        public static string Decrypt(string pToDecrypt, string psKey)
        {
            if (pToDecrypt == string.Empty && pToDecrypt == null && Convert.IsDBNull(pToDecrypt))
            {
                return pToDecrypt;
            }

            DESCryptoServiceProvider aDes = new DESCryptoServiceProvider();
            aDes.Key = ASCIIEncoding.ASCII.GetBytes(psKey);
            aDes.IV = ASCIIEncoding.ASCII.GetBytes(psKey);

            MemoryStream aMemoryStream = new MemoryStream();
            CryptoStream aCryptoStream = new CryptoStream(aMemoryStream, aDes.CreateDecryptor(), CryptoStreamMode.Write);
            StringBuilder aSb = new StringBuilder();
            byte[] inputByteArray = new byte[pToDecrypt.Length / 2];
            for (int i = 0; i < pToDecrypt.Length / 2; i++)
            {
                int iTemp = Convert.ToInt32(pToDecrypt.Substring(i * 2, 2), 16);
                inputByteArray[i] = Convert.ToByte(iTemp);
            }
            aCryptoStream.Write(inputByteArray, 0, inputByteArray.Length);
            aCryptoStream.FlushFinalBlock();
            foreach (byte aByte in aMemoryStream.ToArray())
            {
                aSb.Append(Convert.ToChar(aByte));
            }
            return aSb.ToString();
        }

        /// <summary>
        /// Ms the d5 hash ecrypt.
        /// </summary>
        /// <param name="pToEncrypt">The p to encrypt.</param>
        /// <returns></returns>
        public static string MD5HashEcrypt(string pToEncrypt)
        {
            if (pToEncrypt == string.Empty && pToEncrypt == null && Convert.IsDBNull(pToEncrypt))
            {
                return pToEncrypt;
            }
            try
            {
                MD5CryptoServiceProvider aMD5Hash = new MD5CryptoServiceProvider();
                byte[] inputByteArray = Encoding.UTF8.GetBytes(pToEncrypt);
                byte[] aResult = aMD5Hash.ComputeHash(inputByteArray);
                StringBuilder aSb = new StringBuilder();
                foreach (byte aByte in aResult)
                {
                    aSb.AppendFormat("{0:X2}", aByte);
                }
                return aSb.ToString();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// SHAs the hash ecrypt.
        /// </summary>
        /// <param name="pToEncrypt">The p to encrypt.</param>
        /// <returns></returns>
        public static string SHAHashEcrypt(string pToEncrypt)
        {
            if (pToEncrypt == string.Empty && pToEncrypt == null && Convert.IsDBNull(pToEncrypt))
            {
                return pToEncrypt;
            }
            try
            {
                SHA1CryptoServiceProvider aSHA1 = new SHA1CryptoServiceProvider();
                byte[] inputByteArray = Encoding.UTF8.GetBytes(pToEncrypt);
                byte[] aResult = aSHA1.ComputeHash(inputByteArray);
                StringBuilder aSb = new StringBuilder();
                foreach (byte aByte in aResult)
                {
                    aSb.AppendFormat("{0:X2}", aByte);
                }
                return aSb.ToString();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Ms the d5 verify.
        /// </summary>
        /// <param name="pVerify">The p verify.</param>
        /// <param name="pHash">The p hash.</param>
        /// <returns></returns>
        public static bool MD5Verify(string pVerify, string pHash)
        {
            string aHashOfInput = MD5HashEcrypt(pVerify);
            if (0 == String.CompareOrdinal(aHashOfInput, pHash))
                return true;
            else
                return false;
        }

        /// <summary>
        /// SHAs the verify.
        /// </summary>
        /// <param name="pVerify">The p verify.</param>
        /// <param name="pHash">The p hash.</param>
        /// <returns></returns>
        public static bool SHAVerify(string pVerify, string pHash)
        {
            string aHashOfInput = SHAHashEcrypt(pVerify);
            if (0 == String.CompareOrdinal(aHashOfInput, pHash))
                return true;
            else
                return false;
        }

        /// <summary>
        /// Creates MD5 checksum from given data.
        /// </summary>
        /// <author>ThangTran</author>
        /// <history>
        /// 2011.08.18 - ThangTran: added.
        /// </history>
        public static byte[] CreateMd5Checksum(byte[] data)
        {
            // --- Begin parameters checking code -----------------------------
            Debug.Assert(data != null);
            // --- End parameters checking code -------------------------------

            MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider();
            return provider.ComputeHash(data);
        }

        /// <summary>
        /// Creates two arrays of bytes.
        /// </summary>
        /// <author>ThangTran</author>
        /// <history>
        /// 2011.08.18 - ThangTran: added.
        /// </history>
        public static bool CompareBytes(byte[] data1, byte[] data2)
        {
            // --- Begin parameters checking code -----------------------------
            Debug.Assert(data1 != null);
            Debug.Assert(data2 != null);
            // --- End parameters checking code -------------------------------

            // check the length first
            if (data1.Length != data2.Length)
            {
                return false;
            }

            // compare byte-by-byte
            // do not use enumerator, it will slow the speed (a little bit)
            for (int i = 0; i < data1.Length; i++)
            {
                if (data1[i] != data2[i])
                {
                    return false;
                }
            }

            // they are the same
            return true;
        }
    }

    //----------------------------------------------------------------
    // Class Utility for Generate random password
    // * Date Created	: 05/09/2007
    // * Created by		: Kevin Stewart
    // * Review by		: None
    // * Third Party	: None
    //----------------------------------------------------------------    
    public class PasswordGenerator
    {
        public PasswordGenerator()
        {
            this.Minimum = DefaultMinimum;
            this.Maximum = DefaultMaximum;
            this.ConsecutiveCharacters = false;
            this.RepeatCharacters = true;
            this.ExcludeSymbols = false;
            this.Exclusions = null;

            rng = new RNGCryptoServiceProvider();
        }

        protected int GetCryptographicRandomNumber(int lBound, int uBound)
        {
            // Assumes lBound >= 0 && lBound < uBound
            // returns an int >= lBound and < uBound
            uint urndnum;
            byte[] rndnum = new Byte[4];
            if (lBound == uBound - 1)
            {
                // test for degenerate case where only lBound can be returned
                return lBound;
            }

            uint xcludeRndBase = (uint.MaxValue -
                (uint.MaxValue % (uint)(uBound - lBound)));

            do
            {
                rng.GetBytes(rndnum);
                urndnum = System.BitConverter.ToUInt32(rndnum, 0);
            } while (urndnum >= xcludeRndBase);

            return (int)(urndnum % (uBound - lBound)) + lBound;
        }

        protected char GetRandomCharacter()
        {
            int upperBound = pwdCharArray.GetUpperBound(0);

            if (true == this.ExcludeSymbols)
            {
                upperBound = PasswordGenerator.UBoundDigit;
            }

            int randomCharPosition = GetCryptographicRandomNumber(
                pwdCharArray.GetLowerBound(0), upperBound);

            char randomChar = pwdCharArray[randomCharPosition];

            return randomChar;
        }

        public string Generate()
        {
            // Pick random length between minimum and maximum   
            int pwdLength = GetCryptographicRandomNumber(this.Minimum,
                this.Maximum);

            StringBuilder pwdBuffer = new StringBuilder();
            pwdBuffer.Capacity = this.Maximum;

            // Generate random characters
            char lastCharacter, nextCharacter;

            // Initial dummy character flag
            lastCharacter = nextCharacter = '\n';

            for (int i = 0; i < pwdLength; i++)
            {
                nextCharacter = GetRandomCharacter();

                if (false == this.ConsecutiveCharacters)
                {
                    while (lastCharacter == nextCharacter)
                    {
                        nextCharacter = GetRandomCharacter();
                    }
                }

                if (false == this.RepeatCharacters)
                {
                    string temp = pwdBuffer.ToString();
                    int duplicateIndex = temp.IndexOf(nextCharacter);
                    while (-1 != duplicateIndex)
                    {
                        nextCharacter = GetRandomCharacter();
                        duplicateIndex = temp.IndexOf(nextCharacter);
                    }
                }

                if ((null != this.Exclusions))
                {
                    while (-1 != this.Exclusions.IndexOf(nextCharacter))
                    {
                        nextCharacter = GetRandomCharacter();
                    }
                }

                pwdBuffer.Append(nextCharacter);
                lastCharacter = nextCharacter;
            }

            if (null != pwdBuffer)
            {
                return pwdBuffer.ToString();
            }
            else
            {
                return String.Empty;
            }
        }

        public string Exclusions
        {
            get { return this.exclusionSet; }
            set { this.exclusionSet = value; }
        }

        public int Minimum
        {
            get { return this.minSize; }
            set
            {
                this.minSize = value;
                if (PasswordGenerator.DefaultMinimum > this.minSize)
                {
                    this.minSize = PasswordGenerator.DefaultMinimum;
                }
            }
        }

        public int Maximum
        {
            get { return this.maxSize; }
            set
            {
                this.maxSize = value;
                if (this.minSize >= this.maxSize)
                {
                    this.maxSize = PasswordGenerator.DefaultMaximum;
                }
            }
        }

        public bool ExcludeSymbols
        {
            get { return this.hasSymbols; }
            set { this.hasSymbols = value; }
        }

        public bool RepeatCharacters
        {
            get { return this.hasRepeating; }
            set { this.hasRepeating = value; }
        }

        public bool ConsecutiveCharacters
        {
            get { return this.hasConsecutive; }
            set { this.hasConsecutive = value; }
        }

        private const int DefaultMinimum = 6;
        private const int DefaultMaximum = 10;
        private const int UBoundDigit = 61;

        private RNGCryptoServiceProvider rng;
        private int minSize;
        private int maxSize;
        private bool hasRepeating;
        private bool hasConsecutive;
        private bool hasSymbols;
        private string exclusionSet;

        private char[] pwdCharArray = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();
    }
}
